<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>xmake</title>
  <link rel="icon" href="/assets/img/favicon.ico">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
  <meta name="description" content="Description">
  <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <link href="//cdn.jsdelivr.net/npm/github-markdown-css@4.0.0/github-markdown.min.css" rel="stylesheet">
  <style>
	.markdown-body {
		box-sizing: border-box;
		min-width: 200px;
		max-width: 980px;
		margin: 0 auto;
		padding: 45px;
	}

	@media (max-width: 767px) {
		.markdown-body {
			padding: 15px;
		}
	}
  </style>
</head>
<body>
<article class="markdown-body">
<h4>This is a mirror page, please see the original page: </h4><a href="https://xmake.io/#/zh-cn/manual/extension_modules">https://xmake.io/#/zh-cn/manual/extension_modules</a>
</br>
    <script async type="text/javascript" src="//cdn.carbonads.com/carbon.js?serve=CE7I52QU&placement=xmakeio" id="_carbonads_js"></script>
<style>
#carbonads {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu,
  Cantarell, "Helvetica Neue", Helvetica, Arial, sans-serif;
}

#carbonads {
  display: flex;
  max-width: 330px;
  background-color: hsl(0, 0%, 98%);
  box-shadow: 0 1px 4px 1px hsla(0, 0%, 0%, .1);
}

#carbonads a {
  color: inherit;
  text-decoration: none;
}

#carbonads a:hover {
  color: inherit;
}

#carbonads span {
  position: relative;
  display: block;
  overflow: hidden;
}

#carbonads .carbon-wrap {
  display: flex;
}

.carbon-img {
  display: block;
  margin: 0;
  line-height: 1;
}

.carbon-img img {
  display: block;
}

.carbon-text {
  font-size: 13px;
  padding: 10px;
  line-height: 1.5;
  text-align: left;
}

.carbon-poweredby {
  display: block;
  padding: 8px 10px;
  background: repeating-linear-gradient(-45deg, transparent, transparent 5px, hsla(0, 0%, 0%, .025) 5px, hsla(0, 0%, 0%, .025) 10px) hsla(203, 11%, 95%, .4);
  text-align: center;
  text-transform: uppercase;
  letter-spacing: .5px;
  font-weight: 600;
  font-size: 9px;
  line-height: 1;
}
</style>
    <p>所有扩展模块的使用，都需要通过<a href="/mirror/zh-cn/manual/builtin_modules.html#import">import</a>接口，进行导入后才能使用。</p>
<h3 id="corebaseoption">core.base.option</h3>
<p>一般用于获取xmake命令参数选项的值，常用于插件开发。</p>
<h4 id="optionget">option.get</h4>
<ul>
<li>获取参数选项值</li>
</ul>
<p>在插件开发中用于获取参数选项值，例如：</p>
<pre><code class="lang-lua">-- 导入选项模块
import("core.base.option")

-- 插件入口函数
function main(...)
    print(option.get("info"))
end
</code></pre>
<p>上面的代码获取hello插件，执行：<code>xmake hello --info=xxxx</code> 命令时候传入的<code>--info=</code>选项的值，并显示：<code>xxxx</code></p>
<p>对于非main入口的task任务或插件，可以这么使用：</p>
<pre><code class="lang-lua">task("hello")
    on_run(function ())
        import("core.base.option")
        print(option.get("info"))
    end)
</code></pre>
<h3 id="corebaseglobal">core.base.global</h3>
<p>用于获取xmake全局的配置信息，也就是<code>xmake g|global --xxx=val</code> 传入的参数选项值。</p>
<p>!> 2.1.5版本之前为<code>core.project.global</code>。</p>
<h4 id="globalget">global.get</h4>
<ul>
<li>获取指定配置值</li>
</ul>
<p>类似<a href="#configget">config.get</a>，唯一的区别就是这个是从全局配置中获取。</p>
<h4 id="globalload">global.load</h4>
<ul>
<li>加载配置</li>
</ul>
<p>类似<a href="#globalget">global.get</a>，唯一的区别就是这个是从全局配置中加载。</p>
<h4 id="globaldirectory">global.directory</h4>
<ul>
<li>获取全局配置信息目录</li>
</ul>
<p>默认为<code>~/.config</code>目录。</p>
<h4 id="globaldump">global.dump</h4>
<ul>
<li>打印输出所有全局配置信息</li>
</ul>
<p>输出结果如下：</p>
<pre><code class="lang-lua">{
    clean = true
,   ccache = "ccache"
,   xcode_dir = "/Applications/Xcode.app"
}
</code></pre>
<h3 id="corebasetask">core.base.task</h3>
<p>用于任务操作，一般用于在自定义脚本中、插件任务中，调用运行其他task任务。</p>
<p>!> 2.1.5版本之前为<code>core.project.task</code>。</p>
<h4 id="taskrun">task.run</h4>
<ul>
<li>运行指定任务</li>
</ul>
<p>用于在自定义脚本、插件任务中运行<a href="#task">task</a>定义的任务或插件，例如：</p>
<pre><code class="lang-lua">task("hello")
    on_run(function ()
        print("hello xmake!")
    end)

target("demo")
    on_clean(function(target)

        -- 导入task模块
        import("core.base.task")

        -- 运行这个hello task
        task.run("hello")
    end)
</code></pre>
<p>我们还可以在运行任务时，增加参数传递，例如：</p>
<pre><code class="lang-lua">task("hello")
    on_run(function (arg1, arg2)
        print("hello xmake: %s %s!", arg1, arg2)
    end)

target("demo")
    on_clean(function(target)

        -- 导入task
        import("core.base.task")

        -- {} 这个是给第一种选项传参使用，这里置空，这里在最后面传入了两个参数：arg1, arg2
        task.run("hello", {}, "arg1", "arg2")
    end)
</code></pre>
<p>对于<code>task.run</code>的第二个参数，用于传递命令行菜单中的选项，而不是直接传入<code>function (arg, ...)</code>函数入口中，例如：</p>
<pre><code class="lang-lua">-- 导入task
import("core.base.task")

-- 插件入口
function main(...)

    -- 运行内置的xmake配置任务，相当于：xmake f|config --plat=iphoneos --arch=armv7
    task.run("config", {plat="iphoneos", arch="armv7"})
emd
</code></pre>
<h3 id="corebasejson">core.base.json</h3>
<p>xmake 提供了内置的 json 模块，基于 lua_cjson 实现，我们可以用它实现快速的在 json 和 lua table 直接相互操作。</p>
<p>我们可以通过 <code>import("core.base.json")</code> 直接导入使用。</p>
<p>这里也有一些例子：<a href="https://github.com/xmake-io/xmake/blob/master/tests/modules/json/test.lua">Jsom Examples</a></p>
<h4 id="jsondecode">json.decode</h4>
<p>直接从字符串解码 json 获取 lua table.</p>
<pre><code class="lang-lua">import("core.base.json")
local luatable = json.decode(&#39;[1,"2", {"a":1, "b":true}]&#39;)
print(luatable)
</code></pre>
<pre><code>{
    1.0,
    "2",
    {
      b = true,
      a = 1.0
    }
  }
</code></pre><p>!> 如果里面有 null，可以用 <code>json.null</code> 来判断</p>
<h4 id="jsonencode">json.encode</h4>
<p>我们也可以直接对一个 lua table 进行编码。</p>
<pre><code class="lang-lua">local jsonstr = json.encode({1, "2", {a = 1}}
</code></pre>
<p>需要注意的是，如果需要编码 null，需要使用 <code>json.null</code>，例如</p>
<pre><code class="lang-lua">local jsonstr = json.encode({json.null, 1, "2", false, true})
</code></pre>
<h4 id="jsonloadfile">json.loadfile</h4>
<p>直接加载 json 文件，并解析成 lua table。</p>
<pre><code class="lang-lua">local luatable = json.loadfile("/tmp/xxx.json")
</code></pre>
<h4 id="jsonsavefile">json.savefile</h4>
<p>保存 lua table 到指定 json 文件。</p>
<pre><code class="lang-lua">json.savefile("/tmp/xxx.json", {1, {a = 1}})
</code></pre>
<h3 id="coretoollinker">core.tool.linker</h3>
<p>链接器相关操作，常用于插件开发。</p>
<h4 id="linkerlink">linker.link</h4>
<ul>
<li>执行链接</li>
</ul>
<p>针对target，链接指定对象文件列表，生成对应的目标文件，例如：</p>
<pre><code class="lang-lua">linker.link("binary", "cc", {"a.o", "b.o", "c.o"}, target:targetfile(), {target = target})
</code></pre>
<p>其中<a href="#target">target</a>，为工程目标，这里传入，主要用于获取target特定的链接选项，具体如果获取工程目标对象，见：<a href="#core-project-project">core.project.project</a></p>
<p>当然也可以不指定target，例如：</p>
<pre><code class="lang-lua">linker.link("binary", "cc", {"a.o", "b.o", "c.o"}, "/tmp/targetfile")
</code></pre>
<p>第一个参数指定链接类型，目前支持：binary, static, shared<br>第二个参数告诉链接器，应该作为那种源文件对象进行链接，这些对象源文件使用什么编译器编译的，例如：</p>
<table>
<thead>
<tr>
<th>第二个参数值</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>cc</td>
<td>c编译器</td>
</tr>
<tr>
<td>cxx</td>
<td>c++编译器</td>
</tr>
<tr>
<td>mm</td>
<td>objc编译器</td>
</tr>
<tr>
<td>mxx</td>
<td>objc++编译器</td>
</tr>
<tr>
<td>gc</td>
<td>go编译器</td>
</tr>
<tr>
<td>as</td>
<td>汇编器</td>
</tr>
<tr>
<td>sc</td>
<td>swift编译器</td>
</tr>
<tr>
<td>rc</td>
<td>rust编译器</td>
</tr>
<tr>
<td>dc</td>
<td>dlang编译器</td>
</tr>
</tbody>
</table>
<p>指定不同的编译器类型，链接器会适配最合适的链接器来处理链接，并且如果几种支持混合编译的语言，那么可以同时传入多个编译器类型，指定链接器选择支持这些混合编译语言的链接器进行链接处理：</p>
<pre><code class="lang-lua">linker.link("binary", {"cc", "mxx", "sc"}, {"a.o", "b.o", "c.o"}, "/tmp/targetfile")
</code></pre>
<p>上述代码告诉链接器，a, b, c三个对象文件有可能分别是c, objc++, swift代码编译出来的，链接器会从当前系统和工具链中选择最合适的链接器去处理这个链接过程。</p>
<h4 id="linkerlinkcmd">linker.linkcmd</h4>
<ul>
<li>获取链接命令行字符串</li>
</ul>
<p>直接获取<a href="#linkerlink">linker.link</a>中执行的命令行字符串，相当于：</p>
<pre><code class="lang-lua">local cmdstr = linker.linkcmd("static", "cxx", {"a.o", "b.o", "c.o"}, target:targetfile(), {target = target})
</code></pre>
<p>注：后面<code>{target = target}</code>扩展参数部分是可选的，如果传递了target对象，那么生成的链接命令，会加上这个target配置对应的链接选项。</p>
<p>并且还可以自己传递各种配置，例如：</p>
<pre><code class="lang-lua">local cmdstr = linker.linkcmd("static", "cxx", {"a.o", "b.o", "c.o"}, target:targetfile(), {configs = {linkdirs = "/usr/lib"}})
</code></pre>
<h4 id="linkerlinkargv">linker.linkargv</h4>
<ul>
<li>获取链接命令行参数列表</li>
</ul>
<p>跟<a href="#linkerlinkcmd">linker.linkcmd</a>稍微有点区别的是，此接口返回的是参数列表，table表示，更加方便操作：</p>
<pre><code class="lang-lua">local program, argv = linker.linkargv("static", "cxx", {"a.o", "b.o", "c.o"}, target:targetfile(), {target = target})
</code></pre>
<p>其中返回的第一个值是主程序名，后面是参数列表，而<code>os.args(table.join(program, argv))</code>等价于<code>linker.linkcmd</code>。</p>
<p>我们也可以通过传入返回值给<a href="#os-runv">os.runv</a>来直接运行它：<code>os.runv(linker.linkargv(..))</code></p>
<h4 id="linkerlinkflags">linker.linkflags</h4>
<ul>
<li>获取链接选项</li>
</ul>
<p>获取<a href="#linkerlinkcmd">linker.linkcmd</a>中的链接选项字符串部分，不带shellname和对象文件列表，并且是按数组返回，例如：</p>
<pre><code class="lang-lua">local flags = linker.linkflags("shared", "cc", {target = target})
for _, flag in ipairs(flags) do
    print(flag)
end
</code></pre>
<p>返回的是flags的列表数组。</p>
<h4 id="linkerhas_flags">linker.has_flags</h4>
<ul>
<li>判断指定链接选项是否支持</li>
</ul>
<p>虽然通过<a href="detect-has_flags">lib.detect.has_flags</a>也能判断，但是那个接口更加底层，需要指定链接器名称<br>而此接口只需要指定target的目标类型，源文件类型，它会自动切换选择当前支持的链接器。</p>
<pre><code class="lang-lua">if linker.has_flags(target:targetkind(), target:sourcekinds(), "-L/usr/lib -lpthread") then
    -- ok
end
</code></pre>
<h3 id="coretoolcompiler">core.tool.compiler</h3>
<p>编译器相关操作，常用于插件开发。</p>
<h4 id="compilercompile">compiler.compile</h4>
<ul>
<li>执行编译</li>
</ul>
<p>针对target，链接指定对象文件列表，生成对应的目标文件，例如：</p>
<pre><code class="lang-lua">compiler.compile("xxx.c", "xxx.o", "xxx.h.d", {target = target})
</code></pre>
<p>其中<a href="#target">target</a>，为工程目标，这里传入主要用于获取taeget的特定编译选项，具体如果获取工程目标对象，见：<a href="#core-project-project">core.project.project</a></p>
<p>而<code>xxx.h.d</code>文件用于存储为此源文件的头文件依赖文件列表，最后这两个参数都是可选的，编译的时候可以不传他们：</p>
<pre><code class="lang-lua">compiler.compile("xxx.c", "xxx.o")
</code></pre>
<p>来单纯编译一个源文件。</p>
<h4 id="compilercompcmd">compiler.compcmd</h4>
<ul>
<li>获取编译命令行</li>
</ul>
<p>直接获取<a href="#compilercompile">compiler.compile</a>中执行的命令行字符串，相当于：</p>
<pre><code class="lang-lua">local cmdstr = compiler.compcmd("xxx.c", "xxx.o", {target = target})
</code></pre>
<p>注：后面<code>{target = target}</code>扩展参数部分是可选的，如果传递了target对象，那么生成的编译命令，会加上这个target配置对应的链接选项。</p>
<p>并且还可以自己传递各种配置，例如：</p>
<pre><code class="lang-lua">local cmdstr = compiler.compcmd("xxx.c", "xxx.o", {configs = {includedirs = "/usr/include", defines = "DEBUG"}})
</code></pre>
<p>通过target，我们可以导出指定目标的所有源文件编译命令：</p>
<pre><code class="lang-lua">import("core.project.project")

for _, target in pairs(project.targets()) do
    for sourcekind, sourcebatch in pairs(target:sourcebatches()) do
        for index, objectfile in ipairs(sourcebatch.objectfiles) do
            local cmdstr = compiler.compcmd(sourcebatch.sourcefiles[index], objectfile, {target = target})
        end
    end
end
</code></pre>
<h4 id="compilercompargv">compiler.compargv</h4>
<ul>
<li>获取编译命令行列表</li>
</ul>
<p>跟<a href="#compilercompargv">compiler.compargv</a>稍微有点区别的是，此接口返回的是参数列表，table表示，更加方便操作：</p>
<pre><code class="lang-lua">local program, argv = compiler.compargv("xxx.c", "xxx.o")
</code></pre>
<h4 id="compilercompflags">compiler.compflags</h4>
<ul>
<li>获取编译选项</li>
</ul>
<p>获取<a href="#compilercompcmd">compiler.compcmd</a>中的编译选项字符串部分，不带shellname和文件列表，例如：</p>
<pre><code class="lang-lua">local flags = compiler.compflags(sourcefile, {target = target})
for _, flag in ipairs(flags) do
    print(flag)
end
</code></pre>
<p>返回的是flags的列表数组。</p>
<h4 id="compilerhas_flags">compiler.has_flags</h4>
<ul>
<li>判断指定编译选项是否支持</li>
</ul>
<p>虽然通过<a href="detect-has_flags">lib.detect.has_flags</a>也能判断，但是那个接口更加底层，需要指定编译器名称。<br>而此接口只需要指定语言类型，它会自动切换选择当前支持的编译器。</p>
<pre><code class="lang-lua">-- 判断c语言编译器是否支持选项: -g
if compiler.has_flags("c", "-g") then
    -- ok
end

-- 判断c++语言编译器是否支持选项: -g
if compiler.has_flags("cxx", "-g") then
    -- ok
end
</code></pre>
<h4 id="compilerfeatures">compiler.features</h4>
<ul>
<li>获取所有编译器特性</li>
</ul>
<p>虽然通过<a href="detect-features">lib.detect.features</a>也能获取，但是那个接口更加底层，需要指定编译器名称。<br>而此接口只需要指定语言类型，它会自动切换选择当前支持的编译器，然后获取当前的编译器特性列表。</p>
<pre><code class="lang-lua">-- 获取当前c语言编译器的所有特性
local features = compiler.features("c")

-- 获取当前c++语言编译器的所有特性，启用c++11标准，否则获取不到新标准的特性
local features = compiler.features("cxx", {configs = {cxxflags = "-std=c++11"}})

-- 获取当前c++语言编译器的所有特性，传递工程target的所有配置信息
local features = compiler.features("cxx", {target = target, configs = {defines = "..", includedirs = ".."}})
</code></pre>
<p>所有c编译器特性列表：</p>
<table>
<thead>
<tr>
<th>特性名</th>
</tr>
</thead>
<tbody>
<tr>
<td>c_static_assert</td>
</tr>
<tr>
<td>c_restrict</td>
</tr>
<tr>
<td>c_variadic_macros</td>
</tr>
<tr>
<td>c_function_prototypes</td>
</tr>
</tbody>
</table>
<p>所有c++编译器特性列表：</p>
<table>
<thead>
<tr>
<th>特性名</th>
</tr>
</thead>
<tbody>
<tr>
<td>cxx_variable_templates</td>
</tr>
<tr>
<td>cxx_relaxed_constexpr</td>
</tr>
<tr>
<td>cxx_aggregate_default_initializers</td>
</tr>
<tr>
<td>cxx_contextual_conversions</td>
</tr>
<tr>
<td>cxx_attribute_deprecated</td>
</tr>
<tr>
<td>cxx_decltype_auto</td>
</tr>
<tr>
<td>cxx_digit_separators</td>
</tr>
<tr>
<td>cxx_generic_lambdas</td>
</tr>
<tr>
<td>cxx_lambda_init_captures</td>
</tr>
<tr>
<td>cxx_binary_literals</td>
</tr>
<tr>
<td>cxx_return_type_deduction</td>
</tr>
<tr>
<td>cxx_decltype_incomplete_return_types</td>
</tr>
<tr>
<td>cxx_reference_qualified_functions</td>
</tr>
<tr>
<td>cxx_alignof</td>
</tr>
<tr>
<td>cxx_attributes</td>
</tr>
<tr>
<td>cxx_inheriting_constructors</td>
</tr>
<tr>
<td>cxx_thread_local</td>
</tr>
<tr>
<td>cxx_alias_templates</td>
</tr>
<tr>
<td>cxx_delegating_constructors</td>
</tr>
<tr>
<td>cxx_extended_friend_declarations</td>
</tr>
<tr>
<td>cxx_final</td>
</tr>
<tr>
<td>cxx_nonstatic_member_init</td>
</tr>
<tr>
<td>cxx_override</td>
</tr>
<tr>
<td>cxx_user_literals</td>
</tr>
<tr>
<td>cxx_constexpr</td>
</tr>
<tr>
<td>cxx_defaulted_move_initializers</td>
</tr>
<tr>
<td>cxx_enum_forward_declarations</td>
</tr>
<tr>
<td>cxx_noexcept</td>
</tr>
<tr>
<td>cxx_nullptr</td>
</tr>
<tr>
<td>cxx_range_for</td>
</tr>
<tr>
<td>cxx_unrestricted_unions</td>
</tr>
<tr>
<td>cxx_explicit_conversions</td>
</tr>
<tr>
<td>cxx_lambdas</td>
</tr>
<tr>
<td>cxx_local_type_template_args</td>
</tr>
<tr>
<td>cxx_raw_string_literals</td>
</tr>
<tr>
<td>cxx_auto_type</td>
</tr>
<tr>
<td>cxx_defaulted_functions</td>
</tr>
<tr>
<td>cxx_deleted_functions</td>
</tr>
<tr>
<td>cxx_generalized_initializers</td>
</tr>
<tr>
<td>cxx_inline_namespaces</td>
</tr>
<tr>
<td>cxx_sizeof_member</td>
</tr>
<tr>
<td>cxx_strong_enums</td>
</tr>
<tr>
<td>cxx_trailing_return_types</td>
</tr>
<tr>
<td>cxx_unicode_literals</td>
</tr>
<tr>
<td>cxx_uniform_initialization</td>
</tr>
<tr>
<td>cxx_variadic_templates</td>
</tr>
<tr>
<td>cxx_decltype</td>
</tr>
<tr>
<td>cxx_default_function_template_args</td>
</tr>
<tr>
<td>cxx_long_long_type</td>
</tr>
<tr>
<td>cxx_right_angle_brackets</td>
</tr>
<tr>
<td>cxx_rvalue_references</td>
</tr>
<tr>
<td>cxx_static_assert</td>
</tr>
<tr>
<td>cxx_extern_templates</td>
</tr>
<tr>
<td>cxx_func_identifier</td>
</tr>
<tr>
<td>cxx_variadic_macros</td>
</tr>
<tr>
<td>cxx_template_template_parameters</td>
</tr>
</tbody>
</table>
<h4 id="compilerhas_features">compiler.has_features</h4>
<ul>
<li>判断指定的编译器特性是否支持</li>
</ul>
<p>虽然通过<a href="detect-has-features">lib.detect.has_features</a>也能获取，但是那个接口更加底层，需要指定编译器名称。<br>而此接口只需要指定需要检测的特姓名称列表，就能自动切换选择当前支持的编译器，然后判断指定特性在当前的编译器中是否支持。</p>
<pre><code class="lang-lua">if compiler.has_features("c_static_assert") then
    -- ok
end

if compiler.has_features({"c_static_assert", "cxx_constexpr"}, {languages = "cxx11"}) then
    -- ok
end

if compiler.has_features("cxx_constexpr", {target = target, defines = "..", includedirs = ".."}) then
    -- ok
end
</code></pre>
<p>具体特性名有哪些，可以参考：<a href="#compilerfeatures">compiler.features</a>。</p>
<h3 id="coreprojectconfig">core.project.config</h3>
<p>用于获取工程编译时候的配置信息，也就是<code>xmake f|config --xxx=val</code> 传入的参数选项值。</p>
<h4 id="configget">config.get</h4>
<ul>
<li>获取指定配置值</li>
</ul>
<p>用于获取<code>xmake f|config --xxx=val</code>的配置值，例如：</p>
<pre><code class="lang-lua">target("test")
    on_run(function (target)

        -- 导入配置模块
        import("core.project.config")

        -- 获取配置值
        print(config.get("xxx"))
    end)
</code></pre>
<h4 id="configload">config.load</h4>
<ul>
<li>加载配置</li>
</ul>
<p>一般用于插件开发中，插件任务中不像工程的自定义脚本，环境需要自己初始化加载，默认工程配置是没有被加载的，如果要用<a href="#configget">config.get</a>接口获取工程配置，那么需要先：</p>
<pre><code class="lang-lua">
-- 导入配置模块
import("core.project.config")

function main(...)

    -- 先加载工程配置
    config.load()

    -- 获取配置值
    print(config.get("xxx"))
end
</code></pre>
<h4 id="configarch">config.arch</h4>
<ul>
<li>获取当前工程的架构配置</li>
</ul>
<p>也就是获取<code>xmake f|config --arch=armv7</code>的平台配置，相当于<code>config.get("arch")</code>。</p>
<h4 id="configplat">config.plat</h4>
<ul>
<li>获取当前工程的平台配置</li>
</ul>
<p>也就是获取<code>xmake f|config --plat=iphoneos</code>的平台配置，相当于<code>config.get("plat")</code>。</p>
<h4 id="configmode">config.mode</h4>
<ul>
<li>获取当前工程的编译模式配置</li>
</ul>
<p>也就是获取<code>xmake f|config --mode=debug</code>的平台配置，相当于<code>config.get("mode")</code>。</p>
<h4 id="configbuildir">config.buildir</h4>
<ul>
<li>获取当前工程的输出目录配置</li>
</ul>
<p>也就是获取<code>xmake f|config -o /tmp/output</code>的平台配置，相当于<code>config.get("buildir")</code>。</p>
<h4 id="configdirectory">config.directory</h4>
<ul>
<li>获取当前工程的配置信息目录</li>
</ul>
<p>获取工程配置的存储目录，默认为：<code>projectdir/.config</code></p>
<h4 id="configdump">config.dump</h4>
<ul>
<li>打印输出当前工程的所有配置信息</li>
</ul>
<p>输出结果例如：</p>
<pre><code class="lang-lua">{
    sh = "xcrun -sdk macosx clang++"
,   xcode_dir = "/Applications/Xcode.app"
,   ar = "xcrun -sdk macosx ar"
,   small = true
,   object = false
,   arch = "x86_64"
,   xcode_sdkver = "10.12"
,   ex = "xcrun -sdk macosx ar"
,   cc = "xcrun -sdk macosx clang"
,   rc = "rustc"
,   plat = "macosx"
,   micro = false
,   host = "macosx"
,   as = "xcrun -sdk macosx clang"
,   dc = "dmd"
,   gc = "go"
,   openssl = false
,   ccache = "ccache"
,   cxx = "xcrun -sdk macosx clang"
,   sc = "xcrun -sdk macosx swiftc"
,   mm = "xcrun -sdk macosx clang"
,   buildir = "build"
,   mxx = "xcrun -sdk macosx clang++"
,   ld = "xcrun -sdk macosx clang++"
,   mode = "release"
,   kind = "static"
}
</code></pre>
<h3 id="coreprojectproject">core.project.project</h3>
<p>用于获取当前工程的一些描述信息，也就是在<code>xmake.lua</code>工程描述文件中定义的配置信息，例如：<a href="#target">target</a>、<a href="#option">option</a>等。</p>
<h4 id="projectload">project.load</h4>
<ul>
<li>加载工程描述配置</li>
</ul>
<p>仅在插件中使用，因为这个时候还没有加载工程配置信息，在工程目标的自定义脚本中，不需要执行此操作，就可以直接访问工程配置。</p>
<pre><code class="lang-lua">-- 导入工程模块
import("core.project.project")

-- 插件入口
function main(...)

    -- 加载工程描述配置
    project.load()

    -- 访问工程描述，例如获取指定工程目标
    local target = project.target("test")
end
</code></pre>
<p>!> 2.1.5版本后，不在需要，工程加载会自动在合适时机延迟加载。</p>
<h4 id="projectdirectory">project.directory</h4>
<ul>
<li>获取工程目录</li>
</ul>
<p>获取当前工程目录，也就是<code>xmake -P xxx</code>中指定的目录，否则为默认当前<code>xmake</code>命令执行目录。</p>
<p>!> 2.1.5版本后，建议使用<a href="#os-projectdir">os.projectdir</a>来获取。</p>
<h4 id="projecttarget">project.target</h4>
<ul>
<li>获取指定工程目标对象</li>
</ul>
<p>获取和访问指定工程目标配置，例如：</p>
<pre><code class="lang-lua">local target = project.target("test")
if target then

    -- 获取目标名
    print(target:name())

    -- 获取目标目录, 2.1.9版本之后才有
    print(target:targetdir())

    -- 获取目标文件名
    print(target:targetfile())

    -- 获取目标类型，也就是：binary, static, shared
    print(target:targetkind())

    -- 获取目标名
    print(target:name())

    -- 获取目标源文件
    local sourcefiles = target:sourcefiles()

    -- 获取目标安装头文件列表
    local srcheaders, dstheaders = target:headerfiles()

    -- 获取目标依赖
    print(target:get("deps"))
end
</code></pre>
<h4 id="projecttargets">project.targets</h4>
<ul>
<li>获取工程目标对象列表</li>
</ul>
<p>返回当前工程的所有编译目标，例如：</p>
<pre><code class="lang-lua">for targetname, target in pairs(project.targets())
    print(target:targetfile())
end
</code></pre>
<h4 id="projectoption">project.option</h4>
<ul>
<li>获取指定选项对象</li>
</ul>
<p>获取和访问工程中指定的选项对象，例如：</p>
<pre><code class="lang-lua">local option = project.option("test")
if option:enabled() then
    option:enable(false)
end
</code></pre>
<h4 id="projectoptions">project.options</h4>
<ul>
<li>获取工程所有选项对象</li>
</ul>
<p>返回当前工程的所有编译目标，例如：</p>
<pre><code class="lang-lua">for optionname, option in pairs(project.options())
    print(option:enabled())
end
</code></pre>
<h4 id="projectname">project.name</h4>
<ul>
<li>获取当前工程名</li>
</ul>
<p>也就是获取<a href="#set_project">set_project</a>的工程名配置。</p>
<pre><code class="lang-lua">print(project.name())
</code></pre>
<h4 id="projectversion">project.version</h4>
<ul>
<li>获取当前工程版本号</li>
</ul>
<p>也就是获取<a href="#set_version">set_version</a>的工程版本配置。</p>
<pre><code class="lang-lua">print(project.version())
</code></pre>
<h3 id="corelanguagelanguage">core.language.language</h3>
<p>用于获取编译语言相关信息，一般用于代码文件的操作。</p>
<h4 id="languageextensions">language.extensions</h4>
<ul>
<li>获取所有语言的代码后缀名列表</li>
</ul>
<p>获取结果如下：</p>
<pre><code class="lang-lua">{
     [".c"]      = cc
,    [".cc"]     = cxx
,    [".cpp"]    = cxx
,    [".m"]      = mm
,    [".mm"]     = mxx
,    [".swift"]  = sc
,    [".go"]     = gc
}
</code></pre>
<h4 id="languagetargetkinds">language.targetkinds</h4>
<ul>
<li>获取所有语言的目标类型列表</li>
</ul>
<p>获取结果如下：</p>
<pre><code class="lang-lua">{
     binary = {"ld", "gcld", "dcld"}
,    static = {"ar", "gcar", "dcar"}
,    shared = {"sh", "dcsh"}
}
</code></pre>
<h4 id="languagesourcekinds">language.sourcekinds</h4>
<ul>
<li>获取所有语言的源文件类型列表</li>
</ul>
<p>获取结果如下：</p>
<pre><code class="lang-lua">{
     cc  = ".c"
,    cxx = {".cc", ".cpp", ".cxx"}
,    mm  = ".m"
,    mxx = ".mm"
,    sc  = ".swift"
,    gc  = ".go"
,    rc  = ".rs"
,    dc  = ".d"
,    as  = {".s", ".S", ".asm"}
}
</code></pre>
<h4 id="languagesourceflags">language.sourceflags</h4>
<ul>
<li>加载所有语言的源文件编译选项名列表</li>
</ul>
<p>获取结果如下：</p>
<pre><code class="lang-lua">{
     cc  = {"cflags", "cxflags"}
,    cxx = {"cxxflags", "cxflags"}
,    ...
}
</code></pre>
<h4 id="languageload">language.load</h4>
<ul>
<li>加载指定语言</li>
</ul>
<p>从语言名称加载具体语言对象，例如：</p>
<pre><code class="lang-lua">local lang = language.load("c++")
if lang then
    print(lang:name())
end
</code></pre>
<h4 id="languageload_sk">language.load_sk</h4>
<ul>
<li>从源文件类型加载指定语言</li>
</ul>
<p>从源文件类型：<code>cc, cxx, mm, mxx, sc, gc, as ..</code>加载具体语言对象，例如：</p>
<pre><code class="lang-lua">local lang = language.load_sk("cxx")
if lang then
    print(lang:name())
end
</code></pre>
<h4 id="languageload_ex">language.load_ex</h4>
<ul>
<li>从源文件后缀名加载指定语言</li>
</ul>
<p>从源文件后缀名：<code>.cc, .c, .cpp, .mm, .swift, .go  ..</code>加载具体语言对象，例如：</p>
<pre><code class="lang-lua">local lang = language.load_sk(".cpp")
if lang then
    print(lang:name())
end
</code></pre>
<h4 id="languagesourcekind_of">language.sourcekind_of</h4>
<ul>
<li>获取指定源文件的源文件类型</li>
</ul>
<p>也就是从给定的一个源文件路径，获取它是属于那种源文件类型，例如：</p>
<pre><code class="lang-lua">print(language.sourcekind_of("/xxxx/test.cpp"))
</code></pre>
<p>显示结果为：<code>cxx</code>，也就是<code>c++</code>类型，具体对应列表见：<a href="#languagesourcekinds">language.sourcekinds</a></p>
<h3 id="libdetect">lib.detect</h3>
<p>此模块提供了非常强大的探测功能，用于探测程序、编译器、语言特性、依赖包等。</p>
<p>!> 此模块的接口分散在多个模块目录中，尽量通过导入单个接口来使用，这样效率更高，例如：<code>import("lib.detect.find_package")</code>，而不是通过<code>import("lib.detect")</code>导入所有来调用。</p>
<h4 id="detectfind_file">detect.find_file</h4>
<ul>
<li>查找文件</li>
</ul>
<p>这个接口提供了比<a href="#os-files">os.files</a>更加强大的工程， 可以同时指定多个搜索目录，并且还能对每个目录指定附加的子目录，来模式匹配查找，相当于是<a href="#os-files">os.files</a>的增强版。</p>
<p>例如：</p>
<pre><code class="lang-lua">import("lib.detect.find_file")

local file = find_file("ccache", { "/usr/bin", "/usr/local/bin"})
</code></pre>
<p>如果找到，返回的结果是：<code>/usr/bin/ccache</code></p>
<p>它同时也支持模式匹配路径，进行递归查找，类似<code>os.files</code>：</p>
<pre><code class="lang-lua">local file = find_file("test.h", { "/usr/include", "/usr/local/include/**"})
</code></pre>
<p>不仅如此，里面的路径也支持内建变量，来从环境变量和注册表中获取路径进行查找：</p>
<pre><code class="lang-lua">local file = find_file("xxx.h", { "$(env PATH)", "$(reg HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\XXXX;Name)"})
</code></pre>
<p>如果路径规则比较复杂多变，还可以通过自定义脚本来动态生成路径传入：</p>
<pre><code class="lang-lua">local file = find_file("xxx.h", { "$(env PATH)", function () return val("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\XXXX;Name"):match("\"(.-)\"") end})
</code></pre>
<p>大部分场合下，上面的使用已经满足各种需求了，如果还需要一些扩展功能，可以通过传入第三个参数，自定义一些可选配置，例如：</p>
<pre><code class="lang-lua">local file = find_file("test.h", { "/usr", "/usr/local"}, {suffixes = {"/include", "/lib"}})
</code></pre>
<p>通过指定suffixes子目录列表，可以扩展路径列表（第二个参数），使得实际的搜索目录扩展为：</p>
<pre><code>/usr/include
/usr/lib
/usr/local/include
/usr/local/lib
</code></pre><p>并且不用改变路径列表，就能动态切换子目录来搜索文件。</p>
<p>!> 我们也可以通过<code>xmake lua</code>插件来快速调用和测试此接口：<code>xmake lua lib.detect.find_file test.h /usr/local</code></p>
<h4 id="detectfind_path">detect.find_path</h4>
<ul>
<li>查找路径</li>
</ul>
<p>这个接口的用法跟<a href="#detectfind_file">lib.detect.find_file</a>类似，唯一的区别是返回的结果不同。<br>此接口查找到传入的文件路径后，返回的是对应的搜索路径，而不是文件路径本身，一般用于查找文件对应的父目录位置。</p>
<pre><code class="lang-lua">import("lib.detect.find_path")

local p = find_path("include/test.h", { "/usr", "/usr/local"})
</code></pre>
<p>上述代码如果查找成功，则返回：<code>/usr/local</code>，如果<code>test.h</code>在<code>/usr/local/include/test.h</code>的话。</p>
<p>还有一个区别就是，这个接口传入不只是文件路径，还可以传入目录路径来查找：</p>
<pre><code class="lang-lua">local p = find_path("lib/xxx", { "$(env PATH)", "$(reg HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\XXXX;Name)"})
</code></pre>
<p>同样，此接口也支持模式匹配和后缀子目录：</p>
<pre><code class="lang-lua">local p = find_path("include/*.h", { "/usr", "/usr/local/**"}, {suffixes = "/subdir"})
</code></pre>
<h4 id="detectfind_library">detect.find_library</h4>
<ul>
<li>查找库文件</li>
</ul>
<p>此接口用于指定的搜索目录中查找库文件（静态库，动态库），例如：</p>
<pre><code class="lang-lua">import("lib.detect.find_library")

local library = find_library("crypto", {"/usr/lib", "/usr/local/lib"})
</code></pre>
<p>在macosx上运行，返回的结果如下：</p>
<pre><code class="lang-lua">{
    filename = libcrypto.dylib
,   linkdir = /usr/lib
,   link = crypto
,   kind = shared
}
</code></pre>
<p>如果不指定是否需要静态库还是动态库，那么此接口会自动选择一个存在的库（有可能是静态库、也有可能是动态库）进行返回。</p>
<p>如果需要强制指定需要查找的库类型，可以指定kind参数为（<code>static/shared</code>）：</p>
<pre><code class="lang-lua">local library = find_library("crypto", {"/usr/lib", "/usr/local/lib"}, {kind = "static"})
</code></pre>
<p>此接口也支持suffixes后缀子目录搜索和模式匹配操作：</p>
<pre><code class="lang-lua">local library = find_library("cryp*", {"/usr", "/usr/local"}, {suffixes = "/lib"})
</code></pre>
<h4 id="detectfind_program">detect.find_program</h4>
<ul>
<li>查找可执行程序</li>
</ul>
<p>这个接口比<a href="#detectfind_tool">lib.detect.find_tool</a>较为原始底层，通过指定的参数目录来查找可执行程序。</p>
<pre><code class="lang-lua">import("lib.detect.find_program")

local program = find_program("ccache")
</code></pre>
<p>上述代码犹如没有传递搜索目录，所以它会尝试直接执行指定程序，如果运行ok，那么直接返回：<code>ccache</code>，表示查找成功。</p>
<p>指定搜索目录，修改尝试运行的检测命令参数（默认是：<code>ccache --version</code>）：</p>
<pre><code class="lang-lua">local program = find_program("ccache", {pathes = {"/usr/bin", "/usr/local/bin"}, check = "--help"})
</code></pre>
<p>上述代码会尝试运行：<code>/usr/bin/ccache --help</code>，如果运行成功，则返回：<code>/usr/bin/ccache</code>。</p>
<p>如果<code>--help</code>也没法满足需求，有些程序没有<code>--version/--help</code>参数，那么可以自定义运行脚本，来运行检测：</p>
<pre><code class="lang-lua">local program = find_program("ccache", {pathes = {"/usr/bin", "/usr/local/bin"}, check = function (program) os.run("%s -h", program) end})
</code></pre>
<p>同样，搜索路径列表支持内建变量和自定义脚本：</p>
<pre><code class="lang-lua">local program = find_program("ccache", {pathes = {"$(env PATH)", "$(reg HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug;Debugger)"}})
local program = find_program("ccache", {pathes = {"$(env PATH)", function () return "/usr/local/bin" end}})
</code></pre>
<p><p class="tip"><br>为了加速频发查找的效率，此接口是默认自带cache的，所以就算频繁查找相同的程序，也不会花太多时间。<br>如果要禁用cache，可以在工程目录执行<code>xmake f -c</code>清除本地cache。<br></p>

</p>
<p>我们也可以通过<code>xmake lua lib.detect.find_program ccache</code> 来快速测试。</p>
<h4 id="detectfind_programver">detect.find_programver</h4>
<ul>
<li>查找可执行程序版本号</li>
</ul>
<pre><code class="lang-lua">import("lib.detect.find_programver")

local programver = find_programver("ccache")
</code></pre>
<p>返回结果为：3.2.2</p>
<p>默认它会通过<code>ccache --version</code>尝试获取版本，如果不存在此参数，可以自己指定其他参数：</p>
<pre><code class="lang-lua">local version = find_programver("ccache", {command = "-v"})
</code></pre>
<p>甚至自定义版本获取脚本：</p>
<pre><code class="lang-lua">local version = find_programver("ccache", {command = function () return os.iorun("ccache --version") end})
</code></pre>
<p>对于版本号的提取规则，如果内置的匹配模式不满足要求，也可以自定义：</p>
<pre><code class="lang-lua">local version = find_programver("ccache", {command = "--version", parse = "(%d+%.?%d*%.?%d*.-)%s"})
local version = find_programver("ccache", {command = "--version", parse = function (output) return output:match("(%d+%.?%d*%.?%d*.-)%s") end})
</code></pre>
<p>!> 为了加速频发查找的效率，此接口是默认自带cache的，如果要禁用cache，可以在工程目录执行<code>xmake f -c</code>清除本地cache。</p>
<p>我们也可以通过<code>xmake lua lib.detect.find_programver ccache</code> 来快速测试。</p>
<h4 id="detectfind_package">detect.find_package</h4>
<ul>
<li>查找包文件</li>
</ul>
<p>此接口也是用于查找库文件，但是比<a href="#detectfind_library">lib.detect.find_library</a>更加上层，也更为强大和简单易用，因为它是以包为力度进行查找。</p>
<p>那怎样算是一个完整的包，它包含：</p>
<ol>
<li>多个静态库或者动态库文件</li>
<li>库的搜索目录</li>
<li>头文件的搜索目录</li>
<li>可选的编译链接选项，例如：<code>defines</code>等</li>
<li>可选的版本号</li>
</ol>
<p>例如我们查找一个openssl包：</p>
<pre><code class="lang-lua">import("lib.detect.find_package")

local package = find_package("openssl")
</code></pre>
<p>返回的结果如下：</p>
<pre><code class="lang-lua">{links = {"ssl", "crypto", "z"}, linkdirs = {"/usr/local/lib"}, includedirs = {"/usr/local/include"}}
</code></pre>
<p>如果查找成功，则返回一个包含所有包信息的table，如果失败返回nil</p>
<p>这里的返回结果可以直接作为<code>target:add</code>, <code>option:add</code>的参数传入，用于动态增加<code>target/option</code>的配置：</p>
<pre><code class="lang-lua">option("zlib")
    set_showmenu(true)
    before_check(function (option)
        import("lib.detect.find_package")
        option:add(find_package("zlib"))
    end)
</code></pre>
<pre><code class="lang-lua">target("test")
    on_load(function (target)
        import("lib.detect.find_package")
        target:add(find_package("zlib"))
    end)
</code></pre>
<p>如果系统上装有<code>homebrew</code>, <code>pkg-config</code>等第三方工具，那么此接口会尝试使用它们去改进查找结果。</p>
<p>我们也可以通过指定版本号，来选择查找指定版本的包（如果这个包获取不到版本信息或者没有匹配版本的包，则返回nil）：</p>
<pre><code class="lang-lua">local package = find_package("openssl", {version = "1.0.1"})
</code></pre>
<p>默认情况下查找的包是根据如下规则匹配平台，架构和模式的：</p>
<ol>
<li>如果参数传入指定了<code>{plat = "iphoneos", arch = "arm64", mode = "release"}</code>，则优先匹配，例如：<code>find_package("openssl", {plat = "iphoneos"})</code>。</li>
<li>如果是在当前工程环境，存在配置文件，则优先尝试从<code>config.get("plat")</code>, <code>config.get("arch")</code>和<code>config.get("mode")</code>获取平台架构进行匹配。</li>
<li>最后从<code>os.host()</code>和<code>os.arch()</code>中进行匹配，也就是当前主机的平台架构环境。</li>
</ol>
<p>如果系统的库目录以及<code>pkg-config</code>都不能满足需求，找不到包，那么可以自己手动设置搜索路径：</p>
<pre><code class="lang-lua">local package = find_package("openssl", {linkdirs = {"/usr/lib", "/usr/local/lib"}, includedirs = "/usr/local/include"})
</code></pre>
<p>也可以同时指定需要搜索的链接名，头文件名：</p>
<pre><code class="lang-lua">local package = find_package("openssl", {links = {"ssl", "crypto"}, includes = "ssl.h"}})
</code></pre>
<p>甚至可以指定xmake的<code>packagedir/*.pkg</code>包目录，用于查找对应的<code>openssl.pkg</code>包，一般用于查找内置在工程目录中的本地包。</p>
<p>例如，tbox工程内置了<code>pkg/openssl.pkg</code>本地包载项目中，我们可以通过下面的脚本，传入<code>{packagedirs = ""}</code>参数优先查找本地包，如果找不到再去找系统包。</p>
<pre><code class="lang-lua">target("test")
    on_load(function (target)
        import("lib.detect.find_package")
        target:add(find_package("openssl", {packagedirs = path.join(os.projectdir(), "pkg")}))
    end)
</code></pre>
<p>总结下，现在的查找顺序：</p>
<ol>
<li>如果指定<code>{packagedirs = ""}</code>参数，优先从这个参数指定的路径中查找本地包<code>*.pkg</code></li>
<li>如果在<code>xmake/modules</code>下面存在<code>detect.packages.find_xxx</code>脚本，那么尝试调用此脚本来改进查找结果</li>
<li>如果系统存在vcpkg，优先从vcpkg的包管理系统中去获取包</li>
<li>如果系统存在<code>pkg-config</code>，并且查找的是系统环境的库，则尝试使用<code>pkg-config</code>提供的路径和链接信息进行查找</li>
<li>如果系统存在<code>homebrew</code>，并且查找的是系统环境的库，则尝试使用<code>brew --prefix xxx</code>提供的信息进行查找</li>
<li>从参数中指定的pathes路径和一些已知的系统路径<code>/usr/lib</code>, <code>/usr/include</code>中进行查找</li>
</ol>
<p>这里需要着重说下第二点，通过在<code>detect.packages.find_xxx</code>脚本来改进查找结果，很多时候自动的包探测是没法完全探测到包路径的，<br>尤其是针对windows平台，没有默认的库目录，也没有包管理app，很多库装的时候，都是自己所处放置在系统目录，或者添加注册表项。</p>
<p>因此查找起来没有统一的规则，这个时候，就可以自定义一个查找脚本，去改进<code>find_package</code>的查找机制，对指定包进行更精准的查找。</p>
<p>在xmake自带的<code>xmake/modules/detect/packages</code>目录下，已经有许多的内置包脚本，来对常用的包进行更好的查找支持。<br>当然这不可能满足所有用户的需求，如果用户需要的包还是找不到，那么可以自己定义一个查找脚本，例如：</p>
<p>查找一个名为<code>openssl</code>的包，可以编写一个<code>find_openssl.lua</code>的脚本放置在工程目录：</p>
<pre><code>projectdir
 - xmake
   - modules
     - detect/package/find_openssl.lua
</code></pre><p>然后在工程的<code>xmake.lua</code>文件的开头指定下这个modules的目录：</p>
<pre><code class="lang-lua">add_moduledirs("$(projectdir)/xmake/modules")
</code></pre>
<p>这样xmake就能找到自定义的扩展模块了。</p>
<p>接下来我们看下<code>find_openssl.lua</code>的实现：</p>
<pre><code class="lang-lua">-- imports
import("lib.detect.find_path")
import("lib.detect.find_library")

-- find openssl
--
-- @param opt   the package options. e.g. see the options of find_package()
--
-- @return      see the return value of find_package()
--
function main(opt)

    -- for windows platform
    --
    -- http://www.slproweb.com/products/Win32OpenSSL.html
    --
    if opt.plat == "windows" then

        -- init bits
        local bits = (opt.arch == "x64" and "64" or "32")

        -- init search pathes
        local pathes = {"$(reg HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL %(" .. bits .. "-bit%)_is1;Inno Setup: App Path)",
                        "$(env PROGRAMFILES)/OpenSSL",
                        "$(env PROGRAMFILES)/OpenSSL-Win" .. bits,
                        "C:/OpenSSL",
                        "C:/OpenSSL-Win" .. bits}

        -- find library
        local result = {links = {}, linkdirs = {}, includedirs = {}}
        for _, name in ipairs({"libssl", "libcrypto"}) do
            local linkinfo = find_library(name, pathes, {suffixes = "lib"})
            if linkinfo then
                table.insert(result.links, linkinfo.link)
                table.insert(result.linkdirs, linkinfo.linkdir)
            end
        end

        -- not found?
        if #result.links ~= 2 then
            return
        end

        -- find include
        table.insert(result.includedirs, find_path("openssl/ssl.h", pathes, {suffixes = "include"}))

        -- ok
        return result
    end
end
</code></pre>
<p>里面对windows平台进行注册表读取，去查找指定的库文件，其底层其实也是调用的<a href="#detectfind_library">find_library</a>等接口。</p>
<p>!> 为了加速频发查找的效率，此接口是默认自带cache的，如果要禁用cache，可以在工程目录执行<code>xmake f -c</code>清除本地cache。<br>也可以通过指定force参数，来禁用cache，强制重新查找：<code>find_package("openssl", {force = true})</code></p>
<p>我们也可以通过<code>xmake lua lib.detect.find_package openssl</code> 来快速测试。</p>
<p>2.2.5版本之后，新增了内置接口<a href="#find_packages">find_packages</a>，可以同时查找多个包，并且不需要通过import导入即可直接使用。</p>
<p>并且此版本之后，支持显式的从指定第三方包管理器中查找包，例如：</p>
<pre><code class="lang-lua">find_package("brew::pcre2/libpcre2-8")
</code></pre>
<p>由于每个第三方包管理器的包名不完全一致，比如pcre2在homebrew中有三个库版本，我们可以通过上面的方式，指定查找对应libpcre2-8版本的库。</p>
<p>另外，对于vcpkg, conan也可以通过加上<code>vcpkg::</code>, <code>conan::</code>包命名空间来指定查找里面的库。</p>
<p>v2.5.9 我们也可以通过 <code>find_package("cmake::xxx")</code> 去借助 cmake 来找一些包。</p>
<h4 id="detectfind_tool">detect.find_tool</h4>
<ul>
<li>查找工具</li>
</ul>
<p>此接口也是用于查找可执行程序，不过比<a href="#detectfind_program">lib.detect.find_program</a>更加的高级，功能也更加强大，它对可执行程序进行了封装，提供了工具这个概念：</p>
<ul>
<li>toolname: 工具名，可执行程序的简称，用于标示某个工具，例如：<code>gcc</code>, <code>clang</code>等</li>
<li>program: 可执行程序命令，例如：<code>xcrun -sdk macosx clang</code></li>
</ul>
<p>其对应关系如下：</p>
<table>
<thead>
<tr>
<th>toolname</th>
<th>program</th>
</tr>
</thead>
<tbody>
<tr>
<td>clang</td>
<td><code>xcrun -sdk macosx clang</code></td>
</tr>
<tr>
<td>gcc</td>
<td><code>/usr/toolchains/bin/arm-linux-gcc</code></td>
</tr>
<tr>
<td>link</td>
<td><code>link.exe -lib</code></td>
</tr>
</tbody>
</table>
<p><a href="#detectfind_program">lib.detect.find_program</a>只能通过传入的原始program命令或路径，去判断该程序是否存在。<br>而<code>find_tool</code>则可以通过更加一致的toolname去查找工具，并且返回对应的program完整命令路径，例如：</p>
<pre><code class="lang-lua">import("lib.detect.find_tool")

local tool = find_tool("clang")
</code></pre>
<p>返回的结果为：<code>{name = "clang", program = "clang"}</code>，这个时候还看不出区别，我们可以手动指定可执行的命令：</p>
<pre><code class="lang-lua">local tool = find_tool("clang", {program = "xcrun -sdk macosx clang"})
</code></pre>
<p>返回的结果为：<code>{name = "clang", program = "xcrun -sdk macosx clang"}</code></p>
<p>而在macosx下，gcc就是clang，如果我们执行<code>gcc --version</code>可以看到就是clang的一个马甲，我们可以通过<code>find_tool</code>接口进行智能识别：</p>
<pre><code class="lang-lua">local tool = find_tool("gcc")
</code></pre>
<p>返回的结果为：<code>{name = "clang", program = "gcc"}</code></p>
<p>通过这个结果就可以看的区别来了，工具名实际会被标示为clang，但是可执行的命令用的是gcc。</p>
<p>我们也可以指定<code>{version = true}</code>参数去获取工具的版本，并且指定一个自定义的搜索路径，也支持内建变量和自定义脚本哦：</p>
<pre><code class="lang-lua">local tool = find_tool("clang", {version = true, {pathes = {"/usr/bin", "/usr/local/bin", "$(env PATH)", function () return "/usr/xxx/bin" end}})
</code></pre>
<p>返回的结果为：<code>{name = "clang", program = "/usr/bin/clang", version = "4.0"}</code></p>
<p>这个接口是对<code>find_program</code>的上层封装，因此也支持自定义脚本检测：</p>
<pre><code class="lang-lua">local tool = find_tool("clang", {check = "--help"})
local tool = find_tool("clang", {check = function (tool) os.run("%s -h", tool) end})
</code></pre>
<p>最后总结下，<code>find_tool</code>的查找流程：</p>
<ol>
<li>优先通过<code>{program = "xxx"}</code>的参数来尝试运行和检测。</li>
<li>如果在<code>xmake/modules/detect/tools</code>下存在<code>detect.tools.find_xxx</code>脚本，则调用此脚本进行更加精准的检测。</li>
<li>尝试从<code>/usr/bin</code>，<code>/usr/local/bin</code>等系统目录进行检测。</li>
</ol>
<p>我们也可以在工程<code>xmake.lua</code>中<code>add_moduledirs</code>指定的模块目录中，添加自定义查找脚本，来改进检测机制：</p>
<pre><code>projectdir
  - xmake/modules
    - detect/tools/find_xxx.lua
</code></pre><p>例如我们自定义一个<code>find_7z.lua</code>的查找脚本：</p>
<pre><code class="lang-lua">import("lib.detect.find_program")
import("lib.detect.find_programver")

function main(opt)

    -- init options
    opt = opt or {}

    -- find program
    local program = find_program(opt.program or "7z", opt.pathes, opt.check or "--help")

    -- find program version
    local version = nil
    if program and opt and opt.version then
        version = find_programver(program, "--help", "(%d+%.?%d*)%s")
    end

    -- ok?
    return program, version
end
</code></pre>
<p>将它放置到工程的模块目录下后，执行：<code>xmake l lib.detect.find_tool 7z</code>就可以查找到了。</p>
<p>!> 为了加速频发查找的效率，此接口是默认自带cache的，如果要禁用cache，可以在工程目录执行<code>xmake f -c</code>清除本地cache。</p>
<p>我们也可以通过<code>xmake lua lib.detect.find_tool clang</code> 来快速测试。</p>
<h4 id="detectfind_toolname">detect.find_toolname</h4>
<ul>
<li>查找工具名</li>
</ul>
<p>通过program命令匹配对应的工具名，例如：</p>
<table>
<thead>
<tr>
<th>program</th>
<th>toolname</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>xcrun -sdk macosx clang</code></td>
<td>clang</td>
</tr>
<tr>
<td><code>/usr/bin/arm-linux-gcc</code></td>
<td>gcc</td>
</tr>
<tr>
<td><code>link.exe -lib</code></td>
<td>link</td>
</tr>
<tr>
<td><code>gcc-5</code></td>
<td>gcc</td>
</tr>
<tr>
<td><code>arm-android-clang++</code></td>
<td>clangxx</td>
</tr>
<tr>
<td><code>pkg-config</code></td>
<td>pkg_config</td>
</tr>
</tbody>
</table>
<p>toolname相比program，更能唯一标示某个工具，也方便查找和加载对应的脚本<code>find_xxx.lua</code>。</p>
<h4 id="detectfind_cudadevices">detect.find_cudadevices</h4>
<ul>
<li>查找本机的 CUDA 设备</li>
</ul>
<p>通过 CUDA Runtime API 枚举本机的 CUDA 设备，并查询其属性。</p>
<pre><code class="lang-lua">import("lib.detect.find_cudadevices")

local devices = find_cudadevices({ skip_compute_mode_prohibited = true })
local devices = find_cudadevices({ min_sm_arch = 35, order_by_flops = true })
</code></pre>
<p>返回的结果为：<code>{ { [&#39;$id&#39;] = 0, name = "GeForce GTX 960M", major = 5, minor = 0, ... }, ... }</code></p>
<p>包含的属性依据当前 CUDA 版本会有所不同，可以参考 <a href="https://docs.nvidia.com/cuda/cuda-runtime-api/structcudaDeviceProp.html#structcudaDeviceProp">CUDA 官方文档</a>及其历史版本。</p>
<h4 id="detectfeatures">detect.features</h4>
<ul>
<li>获取指定工具的所有特性</li>
</ul>
<p>此接口跟<a href="#compilerfeatures">compiler.features</a>类似，区别就是此接口更加的原始，传入的参数是实际的工具名toolname。</p>
<p>并且此接口不仅能够获取编译器的特性，任何工具的特性都可以获取，因此更加通用。</p>
<pre><code class="lang-lua">import("lib.detect.features")

local features = features("clang")
local features = features("clang", {flags = "-O0", program = "xcrun -sdk macosx clang"})
local features = features("clang", {flags = {"-g", "-O0", "-std=c++11"}})
</code></pre>
<p>通过传入flags，可以改变特性的获取结果，例如一些c++11的特性，默认情况下获取不到，通过启用<code>-std=c++11</code>后，就可以获取到了。</p>
<p>所有编译器的特性列表，可以见：<a href="#compilerfeatures">compiler.features</a>。</p>
<h4 id="detecthas_features">detect.has_features</h4>
<ul>
<li>判断指定特性是否支持</li>
</ul>
<p>此接口跟<a href="#compilerhas_features">compiler.has_features</a>类似，但是更加原始，传入的参数是实际的工具名toolname。</p>
<p>并且此接口不仅能够判断编译器的特性，任何工具的特性都可以判断，因此更加通用。</p>
<pre><code class="lang-lua">import("lib.detect.has_features")

local features = has_features("clang", "cxx_constexpr")
local features = has_features("clang", {"cxx_constexpr", "c_static_assert"}, {flags = {"-g", "-O0"}, program = "xcrun -sdk macosx clang"})
local features = has_features("clang", {"cxx_constexpr", "c_static_assert"}, {flags = "-g"})
</code></pre>
<p>如果指定的特性列表存在，则返回实际支持的特性子列表，如果都不支持，则返回nil，我们也可以通过指定flags去改变特性的获取规则。</p>
<p>所有编译器的特性列表，可以见：<a href="#compilerfeatures">compiler.features</a>。</p>
<h4 id="detecthas_flags">detect.has_flags</h4>
<ul>
<li>判断指定参数选项是否支持</li>
</ul>
<p>此接口跟<a href="#compilerhas_flags">compiler.has_flags</a>类似，但是更加原始，传入的参数是实际的工具名toolname。</p>
<pre><code class="lang-lua">import("lib.detect.has_flags")

local ok = has_flags("clang", "-g")
local ok = has_flags("clang", {"-g", "-O0"}, {program = "xcrun -sdk macosx clang"})
local ok = has_flags("clang", "-g -O0", {toolkind = "cxx"})
</code></pre>
<p>如果检测通过，则返回true。</p>
<p>此接口的检测做了一些优化，除了cache机制外，大部分场合下，会去拉取工具的选项列表（<code>--help</code>）直接判断，如果选项列表里获取不到的话，才会通过尝试运行的方式来检测。</p>
<h4 id="detecthas_cfuncs">detect.has_cfuncs</h4>
<ul>
<li>判断指定c函数是否存在</li>
</ul>
<p>此接口是<a href="#detectcheck_cxsnippets">lib.detect.check_cxsnippets</a>的简化版本，仅用于检测函数。</p>
<pre><code class="lang-lua">import("lib.detect.has_cfuncs")

local ok = has_cfuncs("setjmp")
local ok = has_cfuncs({"sigsetjmp((void*)0, 0)", "setjmp"}, {includes = "setjmp.h"})
</code></pre>
<p>对于函数的描述规则如下：</p>
<table>
<thead>
<tr>
<th>函数描述</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>sigsetjmp</code></td>
<td>纯函数名</td>
</tr>
<tr>
<td><code>sigsetjmp((void*)0, 0)</code></td>
<td>函数调用</td>
</tr>
<tr>
<td><code>sigsetjmp{int a = 0; sigsetjmp((void*)a, a);}</code></td>
<td>函数名 + {}块</td>
</tr>
</tbody>
</table>
<p>在最后的可选参数中，除了可以指定<code>includes</code>外，还可以指定其他的一些参数用于控制编译检测的选项条件：</p>
<pre><code class="lang-lua">{ verbose = false, target = [target|option], includes = .., configs = {linkdirs = .., links = .., defines = ..}}
</code></pre>
<p>其中verbose用于回显检测信息，target用于在检测前追加target中的配置信息, 而config用于自定义配置跟target相关的编译选项。</p>
<h4 id="detecthas_cxxfuncs">detect.has_cxxfuncs</h4>
<ul>
<li>判断指定c++函数是否存在</li>
</ul>
<p>此接口跟<a href="#detecthas_cfuncs">lib.detect.has_cfuncs</a>类似，请直接参考它的使用说明，唯一区别是这个接口用于检测c++函数。</p>
<h4 id="detecthas_cincludes">detect.has_cincludes</h4>
<ul>
<li>判断指定c头文件是否存在</li>
</ul>
<p>此接口是<a href="#detectcheck_cxsnippets">lib.detect.check_cxsnippets</a>的简化版本，仅用于检测头文件。</p>
<pre><code class="lang-lua">import("lib.detect.has_cincludes")

local ok = has_cincludes("stdio.h")
local ok = has_cincludes({"stdio.h", "stdlib.h"}, {target = target})
local ok = has_cincludes({"stdio.h", "stdlib.h"}, {configs = {defines = "_GNU_SOURCE=1", languages = "cxx11"}})
</code></pre>
<h4 id="detecthas_cxxincludes">detect.has_cxxincludes</h4>
<ul>
<li>判断指定c++头文件是否存在</li>
</ul>
<p>此接口跟<a href="#detecthas_cincludes">lib.detect.has_cincludess</a>类似，请直接参考它的使用说明，唯一区别是这个接口用于检测c++头文件。</p>
<h4 id="detecthas_ctypes">detect.has_ctypes</h4>
<ul>
<li>判断指定c类型是否存在</li>
</ul>
<p>此接口是<a href="#detectcheck_cxsnippets">lib.detect.check_cxsnippets</a>的简化版本，仅用于检测函数。</p>
<pre><code class="lang-lua">import("lib.detect.has_ctypes")

local ok = has_ctypes("wchar_t")
local ok = has_ctypes({"char", "wchar_t"}, {includes = "stdio.h"})
local ok = has_ctypes("wchar_t", {includes = {"stdio.h", "stdlib.h"}, configs = {"defines = "_GNU_SOURCE=1", languages = "cxx11"}})
</code></pre>
<h4 id="detecthas_cxxtypes">detect.has_cxxtypes</h4>
<ul>
<li>判断指定c++类型是否存在</li>
</ul>
<p>此接口跟<a href="#detecthas_ctypes">lib.detect.has_ctypess</a>类似，请直接参考它的使用说明，唯一区别是这个接口用于检测c++类型。</p>
<h4 id="detectcheck_cxsnippets">detect.check_cxsnippets</h4>
<ul>
<li>检测c/c++代码片段是否能够编译通过</li>
</ul>
<p>通用的c/c++代码片段检测接口，通过传入多个代码片段列表，它会自动生成一个编译文件，然后常识对它进行编译，如果编译通过返回true。</p>
<p>对于一些复杂的编译器特性，连<a href="#compilerhas_features">compiler.has_features</a>都无法检测到的时候，可以通过此接口通过尝试编译来检测它。</p>
<pre><code class="lang-lua">import("lib.detect.check_cxsnippets")

local ok = check_cxsnippets("void test() {}")
local ok = check_cxsnippets({"void test(){}", "#define TEST 1"}, {types = "wchar_t", includes = "stdio.h"})
</code></pre>
<p>此接口是<a href="#detecthas_cfuncs">detect.has_cfuncs</a>, <a href="#detecthas_cincludes">detect.has_cincludes</a>和<a href="detect-has_ctypes">detect.has_ctypes</a>等接口的通用版本，也更加底层。</p>
<p>因此我们可以用它来检测：types, functions, includes 还有 links，或者是组合起来一起检测。</p>
<p>第一个参数为代码片段列表，一般用于一些自定义特性的检测，如果为空，则可以仅仅检测可选参数中条件，例如：</p>
<pre><code class="lang-lua">local ok = check_cxsnippets({}, {types = {"wchar_t", "char*"}, includes = "stdio.h", funcs = {"sigsetjmp", "sigsetjmp((void*)0, 0)"}})
</code></pre>
<p>上面那个调用，会去同时检测types, includes和funcs是否都满足，如果通过返回true。</p>
<p>还有其他一些可选参数：</p>
<pre><code class="lang-lua">{ verbose = false, target = [target|option], sourcekind = "[cc|cxx]"}
</code></pre>
<p>其中verbose用于回显检测信息，target用于在检测前追加target中的配置信息, sourcekind 用于指定编译器等工具类型，例如传入<code>cxx</code>强制作为c++代码来检测。</p>
<h3 id="nethttp">net.http</h3>
<p>此模块提供http的各种操作支持，目前提供的接口如下：</p>
<h4 id="httpdownload">http.download</h4>
<ul>
<li>下载http文件</li>
</ul>
<p>这个接口比较简单，就是单纯的下载文件。</p>
<pre><code class="lang-lua">import("net.http")

http.download("https://xmake.io", "/tmp/index.html")
</code></pre>
<h3 id="privilegesudo">privilege.sudo</h3>
<p>此接口用于通过<code>sudo</code>来运行命令，并且提供了平台一致性处理，对于一些需要root权限运行的脚本，可以使用此接口。</p>
<p>!> 为了保证安全性，除非必须使用的场合，其他情况下尽量不要使用此接口。</p>
<h4 id="sudohas">sudo.has</h4>
<ul>
<li>判断sudo是否支持</li>
</ul>
<p>目前仅在<code>macosx/linux</code>下支持sudo，windows上的管理员权限运行暂时还不支持，因此建议使用前可以通过此接口判断支持情况后，针对性处理。</p>
<pre><code class="lang-lua">import("privilege.sudo")

if sudo.has() then
    sudo.run("rm /system/file")
end
</code></pre>
<h4 id="sudorun">sudo.run</h4>
<ul>
<li>安静运行原生shell命令</li>
</ul>
<p>具体用法可参考：<a href="#os-run">os.run</a>。</p>
<pre><code class="lang-lua">import("privilege.sudo")

sudo.run("rm /system/file")
</code></pre>
<h4 id="sudorunv">sudo.runv</h4>
<ul>
<li>安静运行原生shell命令，带参数列表</li>
</ul>
<p>具体用法可参考：<a href="#os-runv">os.runv</a>。</p>
<h4 id="sudoexec">sudo.exec</h4>
<ul>
<li>回显运行原生shell命令</li>
</ul>
<p>具体用法可参考：<a href="#os-exec">os.exec</a>。</p>
<h4 id="sudoexecv">sudo.execv</h4>
<ul>
<li>回显运行原生shell命令，带参数列表</li>
</ul>
<p>具体用法可参考：<a href="#os-execv">os.execv</a>。</p>
<h4 id="sudoiorun">sudo.iorun</h4>
<ul>
<li>安静运行原生shell命令并获取输出内容</li>
</ul>
<p>具体用法可参考：<a href="#os-iorun">os.iorun</a>。</p>
<h4 id="sudoiorunv">sudo.iorunv</h4>
<ul>
<li>安静运行原生shell命令并获取输出内容，带参数列表</li>
</ul>
<p>具体用法可参考：<a href="#os-iorunv">os.iorunv</a>。</p>
<h3 id="develgit">devel.git</h3>
<p>此接口提供了git各种命令的访问接口，相对于直接调用git命令，此模块提供了更加上层易用的封装接口，并且提供对git的自动检测和跨平台处理。</p>
<p>!> 目前windows上，需要手动安装git包后，才能检测到，后续版本会提供自动集成git功能，用户将不用关心如何安装git，就可以直接使用。</p>
<h4 id="gitclone">git.clone</h4>
<ul>
<li>clone代码库</li>
</ul>
<p>此接口对应<code>git clone</code>命令</p>
<pre><code class="lang-lua">import("devel.git")

git.clone("git@github.com:tboox/xmake.git")
git.clone("git@github.com:tboox/xmake.git", {depth = 1, branch = "master", outputdir = "/tmp/xmake"})
</code></pre>
<h4 id="gitpull">git.pull</h4>
<ul>
<li>拉取代码库最新提交</li>
</ul>
<p>此接口对应<code>git pull</code>命令</p>
<pre><code class="lang-lua">import("devel.git")

git.pull()
git.pull({remote = "origin", tags = true, branch = "master", repodir = "/tmp/xmake"})
</code></pre>
<h4 id="gitclean">git.clean</h4>
<ul>
<li>清理代码库文件</li>
</ul>
<p>此接口对应<code>git clean</code>命令</p>
<pre><code class="lang-lua">import("devel.git")

git.clean()
git.clean({repodir = "/tmp/xmake", force = true})
</code></pre>
<h4 id="gitcheckout">git.checkout</h4>
<ul>
<li>签出指定分支版本</li>
</ul>
<p>此接口对应<code>git checkout</code>命令</p>
<pre><code class="lang-lua">import("devel.git")

git.checkout("master", {repodir = "/tmp/xmake"})
git.checkout("v1.0.1", {repodir = "/tmp/xmake"})
</code></pre>
<h4 id="gitrefs">git.refs</h4>
<ul>
<li>获取所有引用列表</li>
</ul>
<p>此接口对应<code>git ls-remote --refs</code>命令</p>
<pre><code class="lang-lua">import("devel.git")

local refs = git.refs(url)
</code></pre>
<h4 id="gittags">git.tags</h4>
<ul>
<li>获取所有标记列表</li>
</ul>
<p>此接口对应<code>git ls-remote --tags</code>命令</p>
<pre><code class="lang-lua">import("devel.git")

local tags = git.tags(url)
</code></pre>
<h4 id="gitbranches">git.branches</h4>
<ul>
<li>获取所有分支列表</li>
</ul>
<p>此接口对应<code>git ls-remote --heads</code>命令</p>
<pre><code class="lang-lua">import("devel.git")

local branches = git.branches(url)
</code></pre>
<h3 id="utilsarchive">utils.archive</h3>
<p>此模块用于压缩和解压缩文件。</p>
<h4 id="archiveextract">archive.extract</h4>
<ul>
<li>解压文件</li>
</ul>
<p>支持大部分常用压缩文件的解压，它会自动检测系统提供了哪些解压工具，然后适配到最合适的解压器对指定压缩文件进行解压操作。</p>
<pre><code class="lang-lua">import("utils.archive")

archive.extract("/tmp/a.zip", "/tmp/outputdir")
archive.extract("/tmp/a.7z", "/tmp/outputdir")
archive.extract("/tmp/a.gzip", "/tmp/outputdir")
archive.extract("/tmp/a.tar.bz2", "/tmp/outputdir")
</code></pre>
<h3 id="utilsplatform">utils.platform</h3>
<p>此模块用于一些平台相关的辅助操作接口</p>
<h4 id="utilsplatformgnu2mslib">utils.platform.gnu2mslib</h4>
<ul>
<li>将 mingw 的 libxxxdll.a 转换成 msvc 的 xxx.lib 库</li>
</ul>
<p>这个功能对 Fortran &amp; C++ 混合项目特别有帮助，因为 VS 不提供fortran编译器，只能用MinGW的gfortran来编译fortran部分，然后和VS的项目链接。<br>往往这样的项目同时有一些其他的库以vs格式提供，因此纯用MinGW编译也不行，只能使用这个功能来混合编译。</p>
<p>而 cmake 也有个类似的 <a href="https://cmake.org/cmake/help/latest/prop_tgt/GNUtoMS.html">GNUtoMS</a>。</p>
<p>相关 issues 见：<a href="https://github.com/xmake-io/xmake/issues/1181">#1181</a></p>
<pre><code class="lang-lua">import("utils.platform.gnu2mslib")

gnu2mslib("xxx.lib", "xxx.dll.a")
gnu2mslib("xxx.lib", "xxx.def")
gnu2mslib("xxx.lib", "xxx.dll.a", {dllname = "xxx.dll", arch = "x64"})
</code></pre>
<p>支持从 def 生成 xxx.lib ，也支持从 xxx.dll.a 自动导出 .def ，然后再生成 xxx.lib</p>
<p>如果不想自动从dll.a生成 def，想借用 gnu linker 生成的 def，那就自己通过 add_shflags("-Wl,--output-def,xxx.def") 配置，生成 def，然后传入 def 到这个接口。。</p>
<p><code>{dllname = xxx, arch = "xxx"}</code> 这些是可选的，根据自己的需求而定。。</p>
<p>也可以直接 xmake l utils.platform.gnu2mslib xxx.lib xxx.dll.a 快速测试验证</p>
</article>
</body>
</html>